package com.example.shop.goods.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.shop.common.api.ServiceReturn;
import com.example.shop.common.api.ServiceStateCode;
import com.example.shop.common.util.CachedThreadPoolUtil;
import com.example.shop.common.util.FileOptionUtil;
import com.example.shop.es.service.ElasticSearchClient;
import com.example.shop.goods.dao.EvaluationDetailBrowseInfoDao;
import com.example.shop.goods.dto.*;
import com.example.shop.goods.dto.Comments;
import com.example.shop.goods.dto.CommentsReply;
import com.example.shop.goods.service.EvaluationService;
import com.example.shop.mapper.*;
import com.example.shop.order.enums.OrderState;

import com.example.shop.pojo.*;
import com.example.shop.rabbitmq.component.OrderComponents;
import com.example.shop.user.dao.UserBriefInfoDao;
import com.example.shop.user.dto.UserBriefInfo;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.rabbitmq.client.GetResponse;
import org.elasticsearch.action.support.WriteRequest;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

@Service
public class EvaluationServiceImpl implements EvaluationService {

    private final GoodsMapper goodsMapper;
    private final UserBriefInfoDao userBriefInfoDao;
    private final OrderMapper orderMapper;
    private final CommentsMapper commentsMapper;
    private final CommentsReplyMapper commentsReplyMapper;
    private final EvaluationMapper evaluationMapper;
    private final EvaluationPictureMapper evaluationPictureMapper;
    private final EvaluationDetailBrowseInfoDao detailBrowseInfoDao;
    private final ElasticSearchClient esClient;
    private final RabbitTemplate rabbitTemplate;
    @Qualifier("mqObjectMapper")
    @Resource
    ObjectMapper objectMapper;

    public EvaluationServiceImpl(GoodsMapper goodsMapper, UserBriefInfoDao userBriefInfoDao, OrderMapper orderMapper, CommentsMapper commentsMapper, CommentsReplyMapper commentsReplyMapper, EvaluationMapper evaluationMapper, EvaluationPictureMapper evaluationPictureMapper, EvaluationDetailBrowseInfoDao detailBrowseInfoDao, ElasticSearchClient esClient, RabbitTemplate rabbitTemplate) {
        this.goodsMapper = goodsMapper;
        this.userBriefInfoDao = userBriefInfoDao;
        this.orderMapper = orderMapper;
        this.commentsMapper = commentsMapper;
        this.commentsReplyMapper = commentsReplyMapper;
        this.evaluationMapper = evaluationMapper;
        this.evaluationPictureMapper = evaluationPictureMapper;
        this.detailBrowseInfoDao = detailBrowseInfoDao;
        this.esClient = esClient;
        this.rabbitTemplate = rabbitTemplate;
    }


    @Override
    public ServiceReturn<Boolean> deleteEvaluation(long evaluationId) {
        int deleteRow = detailBrowseInfoDao.deleteEvaluation(evaluationId);
        if(deleteRow != 0){
            return new ServiceReturn(ServiceStateCode.SUCCESS,true);
        }
        return new ServiceReturn<>(ServiceStateCode.NO_SUCH_RECORD,false);
    }

    @Transactional
    @Override
    public ServiceReturn<EvaluationBrowseInfo> saveEvaluation(User user, EvaluationInfoParam evaluationInfo) {

        //从消息队列中拿取待评价的订单
        Order order = rabbitTemplate.execute(channel -> {
            Long preDeliveryTag = null;
            while (true) {
                //如果该消息队列中没有消息了，则应该不去拉取消息
                if (channel.messageCount(OrderComponents.EVALUATE_QUEUE) == 0) {
                    //如果之前还有消息没有被确认，则将所有未确认的消息进行不确认，返回队列中
                    if (preDeliveryTag != null) {
                        channel.basicNack(preDeliveryTag, true, true);
                    }
                    return null;
                }
                //拉取消息
                GetResponse getResponse = channel.basicGet(OrderComponents.EVALUATE_QUEUE, false);
                //消息内容
                String json = new String(getResponse.getBody(), StandardCharsets.UTF_8);
                //将消息的内容转化为待评价的订单对象
                Order evaluateOrder = objectMapper.readValue(json, Order.class);
                //如果用户评价的订单是该消息，则消费该消息，并返回该订单进行评价
                if(evaluateOrder.getOrderId() == evaluationInfo.getOrderId()){
                    //只确认该消息，其他消息不确认全都重新入队
                    channel.basicAck(getResponse.getEnvelope().getDeliveryTag(), false);
                    //如果之前还有消息没有被确认，则将所有未确认的消息进行不确认，返回队列中
                    if (preDeliveryTag != null) {
                        channel.basicNack(preDeliveryTag, true, true);
                    }
                    return evaluateOrder;
                }
                //记录上一个消息
                preDeliveryTag = getResponse.getEnvelope().getDeliveryTag();
            }
        });
        //对待评价的订单进行评价处理
        if(order!=null){
            Evaluation evaluation = evaluationInfo.toEvaluation();
            long evaluationId = evaluation.getEvaluationId();
            //查找该商品id对应的商家id，用于拼接找到该商家的存放资源目录
            String merchantAccount = goodsMapper.selectAccountById(order.getGoodsId());
            String saveDir = Merchant.RESOURCE_ROOT_DIR+ merchantAccount + Merchant.GOODS_DIR + order.getGoodsId() + Merchant.EVALUATION_DIR + evaluationId + File.separator;
            //该评价的商品对应的订单里的商品
            evaluation.setGoodsId(order.getGoodsId());
            //该评价的用户
            evaluation.setUserId(user.getUserId());

            MultipartFile evaluationVideo = evaluationInfo.getEvaluationVideo();
            //如果评价的视频存在，则对视频处理
            if(evaluationVideo!=null){
                //视频存放的路径
                String videoSavePath = saveDir + "video.mp4";
                evaluation.setEvaluationVideo(videoSavePath);
                try {
                    //将视频保存到指定目录下
                    FileOptionUtil.asyncWrite(videoSavePath,evaluationVideo.getBytes());
                } catch (IOException e) {
                    e.printStackTrace();
                    throw new RuntimeException("Goods模块的EvaluationServiceImpl的saveEvaluation保存视频出错");
                }
            }
            //保存该评价
            evaluationMapper.insert(evaluation);
            //令该商品的对应的评价类型数增加1，同时根据增加的评论类型，对该商品的分值进行增加或者减少
            CachedThreadPoolUtil.submit(()->{
                switch (evaluationInfo.getEvaluationType()){
                    case FAVORABLE:
                        goodsMapper.updateFavorableCommentNumber(evaluation.getGoodsId(),1);
                        break;
                    case MEDIUM:
                        goodsMapper.updateMediumCommentNumber(evaluation.getGoodsId(),1);
                        break;
                    case NEGATION:
                        goodsMapper.updateBadCommentNumber(evaluation.getGoodsId(),1);
                        break;
                }
            });
            //对图片处理
            List<MultipartFile> pictureList = evaluationInfo.getEvaluationPictureList();
            List<EvaluationPicture> evaluationPictureList = new ArrayList<>();
            if(pictureList!=null){
                for(int i=0;i<pictureList.size();i++){
                    //图片的保存路径
                    String pictureSavePath = saveDir + i + ".png";
                    evaluationPictureList.add(new EvaluationPicture(evaluationId,pictureSavePath));
                    try {
                        //将图片保存到指定目录下
                        FileOptionUtil.asyncWrite(pictureSavePath,pictureList.get(i).getBytes());
                    } catch (IOException e) {
                        e.printStackTrace();
                        throw new RuntimeException("Goods模块的EvaluationServiceImpl的saveEvaluation保存图片出错");
                    }
                }
                //保存该评价的图片
                evaluationPictureMapper.batchInsert(evaluationPictureList);
            }
            //评价成功，修改该订单的状态为已评价
            orderMapper.updateFieldById("order_state",OrderState.ALREADY_EVALUATE.getOrderState(),order.getOrderId());

            //返回评价浏览对象
            EvaluationBrowseInfo evaluationBrowseInfo = EvaluationBrowseInfo.getBrowseInfo(UserBriefInfo.getUserBriefInfo(user),evaluation, evaluationPictureList, UserPurchasedGoods.getUserPurchasedGoods(order));
            //将数据更新到es服务器中
            CachedThreadPoolUtil.submit(()->esClient.saveDocument(evaluationBrowseInfo, WriteRequest.RefreshPolicy.IMMEDIATE));
            return new ServiceReturn(ServiceStateCode.SUCCESS,evaluationBrowseInfo);
        }
        //说明该用户根本就没有该订单，即不能进行评论
        return new ServiceReturn(ServiceStateCode.NOT_EXIST_ORDER,null);
    }

    @Transactional
    @Override
    public ServiceReturn<Comments> saveComment(User user, CommentInfoParam commentInfo) {
        QueryWrapper<Evaluation> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("evaluation_id",commentInfo.getEvaluationId());
        boolean exists = evaluationMapper.exists(queryWrapper);
        //是否存在该评价
        if(exists){
            //从评论参数中获取评论
            com.example.shop.pojo.Comments comments = commentInfo.toComment();
            //设置进行评论的用户为当前登录的用户
            comments.setCommentsUserId(user.getUserId());
            //插入到数据库中
            commentsMapper.insert(comments);
            Comments commentView = Comments.getComments(UserBriefInfo.getUserBriefInfo(user), comments);
            //同步到es服务器中
            CachedThreadPoolUtil.submit(()->{
                String script =
                       "if(ctx._source['commentsList'] == null){" +
                               "ctx._source['commentsList'] = [];" +
                       "}" +
                       "ctx._source['commentsList'].add(params.comments)";
                HashMap<String, Object> params = new HashMap<>();
                params.put("comments",commentView);
                EvaluationDetailBrowseInfo detailBrowseInfo = new EvaluationDetailBrowseInfo(comments.getEvaluationId());
                //如果不存在该文档记录，则会报错，但是这个错误是可以接收的
                esClient.updateDocument(detailBrowseInfo,script,params);
            });
            return new ServiceReturn<>(ServiceStateCode.SUCCESS,commentView);
        }
        //说明根本不存在该评价
        return new ServiceReturn(ServiceStateCode.NO_SUCH_EVALUATION,null);
    }

    @Transactional
    @Override
    public ServiceReturn<CommentsReply> saveReplyComment(User user, CommentReplyInfoParam replyCommentInfo) {
        //查看是否存在该评价，并且该评价中是否存在该评论用户或者回复评论或者评价用户
        boolean exists = evaluationMapper.existReplyUser(replyCommentInfo.getEvaluationId(), replyCommentInfo.getReplyUserId());
        UserBriefInfo replyUser = userBriefInfoDao.selectById(replyCommentInfo.getReplyUserId());
        //是否存在该评价
        if(exists && replyUser != null){
            //从回复评论参数中获取回复评论
            com.example.shop.pojo.CommentsReply commentsReply = replyCommentInfo.toCommentReply();
            //设置进行回复评论的用户为当前登录的用户
            commentsReply.setCommentsUserId(user.getUserId());
            //插入到数据库中
            commentsReplyMapper.insert(commentsReply);
            CommentsReply commentsReplyView = CommentsReply.getCommentsReply(UserBriefInfo.getUserBriefInfo(user), replyUser, commentsReply);
            //同步到es服务器中
            CachedThreadPoolUtil.submit(()->{
                String script =
                        "if(ctx._source['commentsReplyList'] == null){" +
                                "ctx._source['commentsReplyList'] = [];" +
                        "}" +
                        "ctx._source['commentsReplyList'].add(params.commentReply)";
                HashMap<String, Object> params = new HashMap<>();
                params.put("commentReply",commentsReplyView);
                EvaluationDetailBrowseInfo detailBrowseInfo = new EvaluationDetailBrowseInfo(commentsReply.getEvaluationId());
                //如果不存在该文档记录，则会报错，但是这个错误是可以接收的
                esClient.updateDocument(detailBrowseInfo,script,params);
            });
            return new ServiceReturn<>(ServiceStateCode.SUCCESS,commentsReplyView);
        }
        return new ServiceReturn(ServiceStateCode.IN_EVALUATION_NO_SUCH_REPLY_USER,null);
    }

}
